#pragma once
#include "PlatformAPI.h"

namespace Alice{
    class ReadScopeLock
    {
    public:
        explicit ReadScopeLock(RWLock& inRWLock)
            : mRWLock(inRWLock)
        {
            mRWLock.ReadLock();
        }

        ~ReadScopeLock()
        {
            mRWLock.ReadUnlock();
        }

    private:
        RWLock& mRWLock;
    };

    /** Keeps a RWLock write-locked while this scope lives */
    class WriteScopeLock
    {
    public:
        explicit WriteScopeLock(RWLock& inRWLock)
            : mRWLock(inRWLock)
        {
            mRWLock.WriteLock();
        }

        ~WriteScopeLock()
        {
            mRWLock.WriteUnlock();
        }
    private:
        RWLock& mRWLock;
    };

    //
    // A scope lifetime controlled Read or Write lock of referenced mutex object
    //
    enum ERWScopeLockType
    {
        SLT_ReadOnly = 0,
        SLT_Write,
    };

    /**
     * Keeps a RWLock read- or write-locked while this scope lives
     *
     *	Notes:
    *		- PThreads and Win32 API's don't provide a mechanism for upgrading a ownership of a read lock to a write lock - to get round that this system unlocks then acquires a write lock so it can race between.
    */
    class RWScopeLock
    {
    public:
        explicit RWScopeLock(RWLock& inRWLock,ERWScopeLockType inERWScopeLockType)
        : mRWLock(inRWLock)
        , mERWScopeLockType(inERWScopeLockType)
        {
            if(mERWScopeLockType != SLT_ReadOnly)
            {
                mRWLock.WriteLock();
            }
            else
            {
                mRWLock.ReadLock();
            }
        }
        
        // NOTE: As the name suggests, this function should be used with caution. 
        // It releases the read lock _before_ acquiring a new write lock. This is not an atomic operation and the caller should 
        // not treat it as such. 
        // E.g. Pointers read from protected data structures prior to this call may be invalid after the function is called. 
        void ReleaseReadOnlyLockAndAcquireWriteLock_USE_WITH_CAUTION()
        {
            if(mERWScopeLockType == SLT_ReadOnly)
            {
                mRWLock.ReadUnlock();
                mRWLock.WriteLock();
                mERWScopeLockType = SLT_Write;
            }
        }
        
        ~RWScopeLock()
        {
            if(mERWScopeLockType == SLT_ReadOnly)
            {
                mRWLock.ReadUnlock();
            }
            else
            {
                mRWLock.WriteUnlock();
            }
        }
        
    private:
        RWLock& mRWLock;
        ERWScopeLockType mERWScopeLockType;
    };
}